#include "memory.h"
#include "stdint.h"
#include "print.h"

#define PG_SIZE 4096

// ================ 位图地址 ================
// 为什么是 0xc009a000?
// 因为 0xc009f000 - 0x1000 - 0x1 = 0xc009e000 这是 PCB 表尾
// 0xc009e000 是将来主线程存放 PCB 的地址（表头 + 4k 为 0xc009efff）
// 因为页单位为 4k，位图以字节为单位划分，细粒度单位是位，每位表示 4k 即一页
// 故而一页大小的位图可管理的内存容量：4k * 8 * 4k = 128M
// 当前我们给虚拟机分配了 32Mb 内存空间，因此这才用了四分之一页
// 为了扩展，本书中这里假设要管理 4页 的位图，即最大可管理 512MB 的物理内存
// 综上所述，0xc009e000 还需要减去 4页 的位图：0xc009e000 - 0x4000 = 0xc009a000
#define MEM_BITMAP_BASE 0xc009a000
// =========================================

// 不太了解
#define K_HEAP_START 0xc0100000

// 内存池结构
struct pool {
    struct bitmap pool_bitmap; // 该内存池所用到的位图结构，用于管理物理内存
    uint32_t phy_addr_start;   // 该内存池所管理物理内存的起始地址
    uint32_t pool_size;        // 该内存池字节容量
};

struct pool kernel_pool, user_pool; // 内核物理内存池和用户物理内存池
struct virtual_addr kernel_vaddr;   // 用于给内核分配虚拟地址

// 初始化内存池
static void mem_pool_init(uint32_t all_mem) {
    put_str("  mem_pool_init start\n");
    uint32_t page_table_size = PG_SIZE * 256;       // 页表大小
    uint32_t used_mem = page_table_size + 0x100000; // 已使用的内存
    uint32_t free_mem = all_mem - used_mem;         // 剩余的可以内存
    uint16_t all_free_pages = free_mem / PG_SIZE;   // 剩余的空闲内存可以分配成多少页

    // 计算内核和用户内存池可以分配到的物理容量，这里采用对半分
    uint16_t kernel_free_pages = all_free_pages / 2;
    uint16_t user_free_pages = all_free_pages - kernel_free_pages;

    // 因为位图每位表示 4k 即一页，而 1byte = 8bit，所以这里除 8 求得位图长度
    uint32_t kbm_length = kernel_free_pages / 8;
    uint32_t ubm_length = user_free_pages / 8;
    
    // 计算内核和用户内存池的起始地址
    uint32_t kp_start = used_mem; // Kernel Pool Start 内核内存池的起始地址
    uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE; // User Pool Start 用户内存池的起始地址

    /* ----------------- 下面都是内核和用户内存池的初始化工作 --------------- */
    kernel_pool.phy_addr_start = kp_start;
    user_pool.phy_addr_start = up_start;

    kernel_pool.pool_size = kernel_free_pages * PG_SIZE;
    user_pool.pool_size = user_free_pages * PG_SIZE;

    kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;
    user_pool.pool_bitmap.btmp_bytes_len = ubm_length;

    /*位图是全局数据，长度不固定。
      全局或静态的数组需要在编译时直到其长度
      而我们需要根据总内存大小算出需要多少字节
      所以改为指定一块内存来生成位图*/
    kernel_pool.pool_bitmap.bits = (void*) MEM_BITMAP_BASE;
    // 用户内存池的位图紧跟在内核内存池的位图之后
    user_pool.pool_bitmap.bits = (void*) (MEM_BITMAP_BASE + kbm_length);

    /* ------------------------- 输出内存池信息 ---------------------------- */
    put_str("      kernel_pool_bitmap_start:");put_int((int)kernel_pool.pool_bitmap.bits);
    put_str(" kernel_pool_phy_addr_start:");put_int(kernel_pool.phy_addr_start);
    put_str("\n");
    put_str("      user_pool_bitmap_start:");put_int((int)user_pool.pool_bitmap.bits);
    put_str(" user_pool_phy_addr_start:");put_int(user_pool.phy_addr_start);
    put_str("\n");

    // 将位图置为 0
    bitmap_init(&kernel_pool.pool_bitmap);
    bitmap_init(&user_pool.pool_bitmap);

    /* ----------- 下面初始化内核虚拟地址的位图，按实际物理内存大小生成数组 ---------- */
    // 用于维护内核堆的虚拟地址，所以要和内核内存池大小一致
    kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;

    // 位图的数组指向一块未使用的内存，目前定位在内核内存池和用户内存池之外
    kernel_vaddr.vaddr_bitmap.bits = (void*) (MEM_BITMAP_BASE + kbm_length + ubm_length);

    kernel_vaddr.vaddr_start = K_HEAP_START;
    bitmap_init(&kernel_vaddr.vaddr_bitmap);
    put_str("   mem_pool_init done\n");
}

// 内存管理部分的初始化入口
void mem_init() {
    put_str("mem_init start\n");
    uint32_t mem_bytes_total = (*(uint32_t*)(0xb00)); // 0xb00 是 loader.S 中定义的 mem_bytes_total 存储总内存容量
    mem_pool_init(mem_bytes_total);	  // 初始化内存池
    put_str("mem_init done\n");
}
